package com.wellsoft.pt.app.dingtalk.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.request.OapiSnsGetuserinfoBycodeRequest;
import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptException;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
import com.taobao.api.ApiException;
import com.wellsoft.context.jdbc.support.QueryItem;
import com.wellsoft.context.util.encode.Md5PasswordEncoderUtils;
import com.wellsoft.context.util.sm.SM3Util;
import com.wellsoft.context.web.controller.BaseFormDataController;
import com.wellsoft.pt.app.dingtalk.constants.DingTalkApis;
import com.wellsoft.pt.app.dingtalk.constants.DingTalkInfo;
import com.wellsoft.pt.app.dingtalk.entity.EventCallBack;
import com.wellsoft.pt.app.dingtalk.entity.MultiOrgDingDept;
import com.wellsoft.pt.app.dingtalk.enums.EnumCallBackEventType;
import com.wellsoft.pt.app.dingtalk.service.EventCallBackService;
import com.wellsoft.pt.app.dingtalk.service.MultiOrgDingDeptService;
import com.wellsoft.pt.app.dingtalk.service.MultiOrgDingUserInfoService;
import com.wellsoft.pt.app.dingtalk.utils.DingtalkApiUtils;
import com.wellsoft.pt.app.dingtalk.utils.DingtalkUtils;
import com.wellsoft.pt.multi.org.entity.MultiOrgUserAccount;
import com.wellsoft.pt.multi.org.facade.service.MultiOrgUserService;
import com.wellsoft.pt.security.core.userdetails.RestfulUserDetailsServiceProvider;
import com.wellsoft.pt.security.core.userdetails.UserDetails;
import com.wellsoft.pt.security.util.SpringSecurityUtils;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * Description: 钉钉接口controller
 *  
 * @author Well
 * @date 2020年5月29日
 * @version 1.0
 * 
 * <pre>
 * 修改记录:
 * 修改后版本	修改人		修改日期			修改内容
 * 2020年5月29日.1	Well		2020年5月29日		Create
 * </pre>
 *
 */
@Controller
@RequestMapping("mobile/pt/dingtalk")
public class DingTalkController extends BaseFormDataController {

	// private Logger logger =
	// LoggerFactory.getLogger(DingTalkController.class);

	@Autowired
	private EventCallBackService eventCallBackService;

	@Autowired
	private MultiOrgDingUserInfoService multiOrgDingUserInfoService;

	@Autowired
	private MultiOrgDingDeptService multiOrgDingDeptService;

	@Autowired
	private RestfulUserDetailsServiceProvider userDetailsServiceProvider;

	@Autowired
	private MultiOrgUserService multiOrgUserService;

	/**
	 * 相应钉钉回调时的值
	 */
	private static final String CALLBACK_RESPONSE_SUCCESS = "success";

	/**
	 * 回调事件监听
	 * 
	 * @return
	 * @throws JSONException
	 */
	@RequestMapping(value = "/call_back", method = RequestMethod.POST)
	// @ResponseBody
	public void callback(HttpServletRequest request, HttpServletResponse response) {
		/** url中的签名 **/
		String msgSignature = request.getParameter("signature");
		/** url中的时间戳 **/
		String timeStamp = request.getParameter("timestamp");
		/** url中的随机字符串 **/
		String nonce = request.getParameter("nonce");

		/** post数据包数据中的加密数据 **/
		ServletInputStream sis;
		StringBuilder sb = new StringBuilder();
		try {
			sis = request.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(sis));
			String line = null;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e1) {
			logger.error(e1.getMessage());
		}
		JSONObject jsonEncrypt = JSONObject.fromObject(sb.toString());
		String encrypt = jsonEncrypt.getString("encrypt");

		/** 对encrypt进行解密 **/
		DingTalkEncryptor dingTalkEncryptor = null;
		String plainText = null;
		try {
			dingTalkEncryptor = new DingTalkEncryptor(DingTalkInfo.EVENT_CALL_BACK_TOKEN, DingTalkInfo.APP_AES_KEY,
					DingTalkInfo.CORP_ID);
			plainText = dingTalkEncryptor.getDecryptMsg(msgSignature, timeStamp, nonce, encrypt);

			/** 对从encrypt解密出来的明文进行处理 **/
			JSONObject plainTextJson = JSONObject.fromObject(plainText);

			String eventType = plainTextJson.getString("EventType");
			// 时间类型为check_url时，不做数据插入，该动作为钉钉测试回调地址，请求成功，直接返回“success”加密数据即可。
			if (!"check_url".equals(eventType.trim())) {
				String dingTimeStamp = plainTextJson.getString("TimeStamp");
				String userId = plainTextJson.get("UserId") == null ? ""
						: plainTextJson.getJSONArray("UserId").toString(); // 用户信息
				String deptId = plainTextJson.get("DeptId") == null ? ""
						: plainTextJson.getJSONArray("DeptId").toString(); // 部门信息
				String corpId = plainTextJson.getString("CorpId");

				// 先保存事件回调的数据，后续通过job任务扫描表数据，进行真正的数据落地，避免数据落地业务复杂，导致平台迟迟不响应钉钉服务器请求二出错
				EventCallBack eventCallBack = new EventCallBack();
				eventCallBack.setEventType(eventType);
				eventCallBack.setDingTimeStamp(dingTimeStamp);
				eventCallBack.setDingUserId(userId);
				eventCallBack.setDingDeptId(deptId);
				eventCallBack.setDingCorpId(corpId);
				eventCallBack.setStatus(0); // 默认为0，未处理
				eventCallBack.setOptTime(new Date());
				this.eventCallBackService.save(eventCallBack);
			}

			/** 对返回信息进行加密 **/
			long timeStampLong = Long.parseLong(timeStamp);
			Map<String, String> jsonMap = null;
			try {
				jsonMap = dingTalkEncryptor.getEncryptedMap(CALLBACK_RESPONSE_SUCCESS, timeStampLong, nonce);
			} catch (DingTalkEncryptException e) {
				logger.error(e.getMessage());
			}
			JSONObject json = new JSONObject();
			json.putAll(jsonMap);
			try {
				response.getWriter().append(json.toString());
			} catch (IOException e) {
				logger.error(e.getMessage());
			}
		} catch (DingTalkEncryptException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}

	@RequestMapping(value = "/auth", method = RequestMethod.GET)
	public void auth(HttpServletRequest request, HttpServletResponse response) {
		String param_uri = request.getParameter("uri");
		String decoded = "";
		try {
			decoded = URLDecoder.decode(param_uri, "utf-8");
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
		}
		String dingtalk_auth_uri = DingTalkApis.URI_SNS_AUTHORIZE;
		String redirect_uri = DingtalkUtils.urlEncode(DingTalkInfo.CORP_DOMAIN_URI + decoded);
		dingtalk_auth_uri = DingtalkUtils.uriFormat(dingtalk_auth_uri, DingTalkInfo.SNS_APP_ID, redirect_uri);
		try {
			response.sendRedirect(dingtalk_auth_uri);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@RequestMapping(value = "/redirect", method = RequestMethod.GET)
	public void redirect(HttpServletRequest request, HttpServletResponse resp) {
		String code = request.getParameter("code");
		DefaultDingTalkClient client = new DefaultDingTalkClient(DingTalkApis.URI_GETUSERINFO_BYCODE);
		OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest();
		req.setTmpAuthCode(code);
		try {
			OapiSnsGetuserinfoBycodeResponse response = client.execute(req, DingTalkInfo.SNS_APP_ID,
					DingTalkInfo.SNS_APP_SECRET);
			JSONObject jb = DingtalkUtils.getJsonResult(response.getBody());
			if (jb != null) {
				JSONObject userObj = jb.getJSONObject("user_info");
				String unionid = userObj.getString("unionid");
				QueryItem item = multiOrgDingUserInfoService.getMultiOrgDingUserInfoByMap(unionid, "");
				if (item != null) {
					String loginName = item.getString("loginname");
					String password = item.getString("password");

					UserDetails userDetails = SpringSecurityUtils.getCurrentUser();
					// 设置access_token
					String access_token = UUID.randomUUID().toString();
					// String localAddr = "http://" + request.getLocalAddr()
					// +":"+ request.getLocalPort() + request.getContextPath();
					try {
						// 生成返回结果
						if (userDetails != null && loginName.equals(userDetails.getLoginName())) {// 已登录
							resp.sendRedirect(DingTalkInfo.CORP_DOMAIN_URI
									+ "web/app/pt-mobile/pt-mobile-base.html?wellpt_access_token=" + access_token);
						}
						// 非单点登录
						MultiOrgUserAccount userFromDb = multiOrgUserService.getUserAccountByLoginName(loginName);
						if (userFromDb != null && (Md5PasswordEncoderUtils.encodePassword(password, loginName)
								.equalsIgnoreCase(userFromDb.getPassword())
								|| org.apache.commons.lang3.StringUtils.equals(
										SM3Util.encrypt(password + "{" + loginName.toLowerCase() + "}"),
										userFromDb.getPassword()))) {

							userDetails = (UserDetails) userDetailsServiceProvider.getUserDetails(loginName);
							UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
									userDetails, password, userDetails.getAuthorities());
							result.setDetails(userFromDb.getPassword());
							SecurityContextHolder.getContext().setAuthentication(result);
							// access_token = UUID.randomUUID().toString();
							SpringSecurityUtils.cacheAccessToken(access_token, result);
							// response.setAccess_token(access_token);
							resp.sendRedirect(DingTalkInfo.CORP_DOMAIN_URI
									+ "web/app/pt-mobile/pt-mobile-base.html?wellpt_access_token=" + access_token);
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		} catch (ApiException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 部门同步
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping(value = "/asynDept", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> asynDept(HttpServletRequest request, HttpServletResponse response) {
		Map<String, Object> map = new HashMap<>();
		map.put("msg", "success");
		try {
			asynDeptDataFromDingtalk(); // 1、同步部门信息
		} catch (Exception e) {
			map.put("msg", "fail");
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 用户同步
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping(value = "/asynUser", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> asynUser(HttpServletRequest request, HttpServletResponse response) {
		Map<String, Object> map = new HashMap<>();
		map.put("msg", "success");
		try {
			asynUserDataFromDingtalk(); // 2、同步用户信息
		} catch (Exception e) {
			map.put("msg", "fail");
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 回调事件注册
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping(value = "/eventRegister", method = RequestMethod.GET)
	@ResponseBody
	public JSONObject eventRegister(HttpServletRequest request, HttpServletResponse response) {
		JSONObject jb = null;
		try {
			List<String> backTags = new ArrayList<>();
			backTags.add(EnumCallBackEventType.user_active_org.getValue());
			backTags.add(EnumCallBackEventType.user_add_org.getValue());
			backTags.add(EnumCallBackEventType.user_leave_org.getValue());
			backTags.add(EnumCallBackEventType.user_modify_org.getValue());
			backTags.add(EnumCallBackEventType.org_admin_add.getValue());
			backTags.add(EnumCallBackEventType.org_admin_remove.getValue());
			backTags.add(EnumCallBackEventType.org_dept_create.getValue());
			backTags.add(EnumCallBackEventType.org_dept_modify.getValue());
			backTags.add(EnumCallBackEventType.org_dept_remove.getValue());
			jb = DingtalkApiUtils.registerCallBack(backTags, DingtalkApiUtils.getAccessToken());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return jb;
	}

	public void asynDeptDataFromDingtalk() {
		String accessToken = DingtalkApiUtils.getAccessToken();
		JSONObject jb = DingtalkApiUtils.getDingTalkDeptList(accessToken);
		JSONObject newJb = null;
		if (jb != null) {
			Object obj = jb.get("department");
			if (obj != null) {
				JSONArray arrays = jb.getJSONArray("department");
				JSONArray arraysNew = new JSONArray();
				for (int i = 0; i < arrays.size(); i++) {
					JSONObject dept = arrays.getJSONObject(i);
					long id = dept.getLong("id");
					JSONArray childs = new JSONArray();
					for (int j = 0; j < arrays.size(); j++) {
						JSONObject deptc = arrays.getJSONObject(j);
						Object parentidc = deptc.get("parentid");
						if (parentidc != null) {
							long pidc = deptc.getLong("parentid");
							if (id == pidc) {
								childs.add(deptc);
							}
						}
					}
					dept.put("childs", childs);
					arraysNew.add(dept);
				}

				for (int n = 0; n < arraysNew.size(); n++) {
					newJb = arraysNew.getJSONObject(n);
					int newId = newJb.getInt("id");
					if (newId == 1) {
						break;
					}
				}
				getNewDept(newJb, arraysNew, 1);
			}
		}
	}

	public void getNewDept(JSONObject newJb, JSONArray arraysNew1, int order) {
		// 保存自己
		this.multiOrgDingDeptService.fetchDeptFromDingtalk(newJb, order, null);
		// 保存child节点
		long objId = newJb.getLong("id");
		int j = 0;
		for (int i = 0; i < arraysNew1.size(); i++) {
			JSONObject itOjb = arraysNew1.getJSONObject(i);
			if (itOjb.get("parentid") == null) {
				continue;
			}
			long parentid = itOjb.getLong("parentid");
			if (objId == parentid) {
				getNewDept(itOjb, arraysNew1, j);
			}
		}
	}

	public void asynUserDataFromDingtalk() {
		String accessToken = DingtalkApiUtils.getAccessToken();
		List<MultiOrgDingDept> deptList = multiOrgDingDeptService.listAll();
		for (MultiOrgDingDept dept : deptList) {
			String deptId = dept.getId();
			JSONObject jb = DingtalkApiUtils.getDingTalkUserListbypage(deptId, accessToken);
			if (jb != null) {
				Object obj = jb.get("userlist");
				if (obj != null) {
					JSONArray arrays = jb.getJSONArray("userlist");
					for (int i = 0; i < arrays.size(); i++) {
						JSONObject userObj = arrays.getJSONObject(i);
						userObj = DingtalkApiUtils.getDingTalkUserInfo(userObj.getString("userid"), accessToken);
						multiOrgDingUserInfoService.addUserFromDingtalk(userObj);
					}
				}
			}
		}
	}

	/**
	 * 回调事件失败结果
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping(value = "/get_call_back_failed_result", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> getCallBackFailedResult(HttpServletRequest request, HttpServletResponse response) {
		Map<String, Object> map = new HashMap<>();
		map.put("msg", "failed");
		try {
			JSONObject jb = DingtalkApiUtils.getCallBackFailedResult(DingtalkApiUtils.getAccessToken());
			int errcode = jb.getInt("errcode");
			if (errcode == 0) {
				map.put("msg", "success");
				JSONArray failed_list = jb.getJSONArray("failed_list");
				for (int i = 0; i < failed_list.size(); i++) {
					JSONObject res = failed_list.getJSONObject(i);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

	public static void main(String[] args) {
		// try {
		// String encode = URLEncoder.encode("mobile/pt/dingtalk/redirect",
		// "utf-8");
		// System.out.println(encode);
		// String decode = URLDecoder.decode(encode, "utf-8");
		// System.out.println(decode);
		// } catch (UnsupportedEncodingException e) {
		// e.printStackTrace();
		// }
		System.out.println(DingtalkUtils.getAesKey(8));
	}

}
